\section{Qt相关}
\subsection{Qt包含目录}
在使用Qt的某一个模块的时候，需要include相应的头文件，为了告诉编译器头文件的具体位置，需要在pro文件中指定需要使用的相关模块，这样qmake会对pro文件进行处理，生成相应的路径。也可以指定不是用某些库。

常见的模块有：core、gui、widgets等，如果想要包含private的头文件，那么就需要接着添加core-private、gui-private、widgets-private等。具体的效果可以使用VS打开pro文件，然后查看相应的头文件设置信息。          

\subsection{预编译文件}
当一个工程项目很大时，会出现很多的头文件，这个时候，把他们全部一次性加入到工程当中是一件非常繁琐和头疼的事情。在Qt当中，可以进行这样的方便设置。将每一个模块的代码（包括h文件和cpp文件）单独放置在一个文件下面，然后再新建一个pri文件，在pri文件当中设置需要include的文件列表。这样，当我们需要使用这个模块时，不需要再把源代码加入到工程当中，只需要把该pri文件include到pro文件当中，这样就可以了。

\subsection{Qtcreator子项目}
在一个较大的工程项目当中，会有多个子项目，同时编译得到最后的文件。这在vs当中被称为solution。qtcreator当中也有类似的功能。

主要的操作是：在顶部的pro文件当中设置模板TEMPLATE为subdirs模式；然后设置SUBDIRS，添加指定的工程模块；使用"CONFIG+=ordered"配置为顺序编译。需要注意的是每一个单独的子项目都需要能够单独编译成功。为了方便，可以将这些工程的目标文件的生成路径设为一致。

这个子项目跟上面的pri文件不同，该文件对应的是一个项目很多时，包含很多模块进行简化。
\subsection{Qt国际化}
在软件当中，需要实现界面的中文显示，这里采用的是Qt的国际化方案，也就是提供针对软件界面文字的中国版本的翻译文件，将国家设为中国就可以了。

简单介绍一下实现的步骤（详细的可以参考网上的教程）：首先，在你需要翻译的地方，字符串需要写在tr函数当中，然后在pro文件当中加入ts文件，接着运行lupdate命令，生成相应的ts文件；然后使用语言家软件打开ts文件，或者你直接打开ts文件进行编辑，设置好相应的翻译；完成之后，运行lrelease命令生成压缩版的翻译文件qm；然后就是怎么使用的问题了，网上有好几种方案，一种就是放在exe文件的运行目录，另一种就是将qm文件放到工程的资源文件当中，这样的好处就是避免了文件的丢失，别人的篡改等等，可以编译进二进制文件当中。而且，如果加入到资源文件当中，每次自动生成qm文件就还在那个位置，就不需要你把它再拷贝到exe目录里。

简单说一下，在load qm文件时遇到的坑。我已经把qm文件加入到了qrc文件当中，但是无论如何都不能正确的载入，网上搜索了各种教程也不行。qm文件其实也拷贝到了文件夹下，也是不行。后来在res文件夹下面新建了一个translations文件夹，然后qm文件都放在了里面，然后接着load就好使了，我也不知道是为什么。

需要注意的是，如果在不同分支当中同时运行了lupdate来更新翻译，由于ts文件会继续翻译短语的所在文件和行号，所以合并的时候，ts文件可能会出现冲突。可以采用一个不生产行号的方法来折中处理。但是为了避免麻烦，还是在单一的分支当中提交翻译吧。
\subsection{如何保存程序的配置信息？}
在程序的运行过程当中，用户可能会对窗口的位置、大小进行一些改变，或者程序的一些信息做了更改，如何在下次启动时能够保持原样？在Qt当中，是通过配置文件实现的。
\subsection{如何在状态栏上添加一些复杂的控件？}
\subsection{如何制作图标？}
SVG格式的图标怎么制作？适用于什么情况？
\subsection{生成注释}
为了方便阅读源代码，需要自动生成注释。从源代码当中提取注释并生成文档的方案是有的，那就是doxygen，但是一个个去添加固定格式的注释简直很烦诶，还需要自动根据代码当中的函数添加注释模版。这个东西貌似需要IDE支持。

1.doxygen的安装

如果IDE是qtcreator的话，可以安装doxygen的插件，直接去\href{https://github.com/fpoussin/qtcreator-doxygen}{Github qtcreator-doxygen}下载。这个插件的编译需要跟qtcreator的源代码一起编译生成，所以你必须下载对应版本的插件，其实就是一个dll文件。

下载好dll文件后，将其放到qtcreator安装目录下的plugins目录，然后重启就可以了。之后，就会在工具菜单下多了一个doxygen子菜单，然后就可以自动地给整个文件添加注释了。这个注释最好添加在源文件当中。

添加完注释之后，就是利用doxygen生成文档了。

2.doxygen的编译

网上仅提供了某些版本的二进制文件，但是没有最新版，如何编译得到最新版qtcreator的doxygen插件？

\subsection{Paint事件}
导致画面闪烁的关键原因分析：

一、绘制窗口由于大小位置状态改变进行重绘操作时，绘图窗口内容或大小每改变一次，都要调用Paint事件进行重绘操作，该操作会使画面重新刷新一次以维持窗口正常显示。刷新过程中会导致所有图元重新绘制，而各个图元的重绘操作并不会导致Paint事件发生，因此窗口的每一次刷新只会调用Paint事件一次。窗口刷新一次的过程中，每一个图元的重绘都会立即显示到窗口，因此整个窗口中，只要是图元所在的位置，都在刷新，而刷新的时间是有差别的，闪烁现象自然会出现。所以说，此时导致窗口闪烁现象的关键因素并不在于Paint事件调用的次数多少，而在于各个图元的重绘。

根据以上分析可知，当图元数目不多时，窗口刷新的位置也不多，窗口闪烁效果并不严重；当图元数目较多时，绘图窗口进行重绘的图元数量增加，绘图窗口每一次刷新都会导致较多的图元重新绘制，窗口的较多位置都在刷新，闪烁现象自然就会越来越严重。特别是图元比较大绘制时间比较长时，闪烁问题会更加严重，因为时间延迟会更长。
解决上述问题的关键在于：窗口刷新一次的过程中，让所有图元同时显示到窗口。

二、进行鼠标跟踪绘制操作或者对图元进行变形操作时，当进行鼠标跟踪绘制操作或者对图元进行变形操作时，Paint事件会频繁发生，这会使窗口的刷新次数大大增加。虽然窗口刷新一次的过程中所有图元同时显示到窗口，但也会有时间延迟，因为此时窗口刷新的时间间隔远小于图元每一次显示到窗口所用的时间。因此闪烁现象并不能完全消除！所以说，此时导致窗口闪烁现象的关键因素在于Paint事件发生的次数多少。
解决此问题的关键在于：设置窗体或控件的几个关键属性。

解决双缓冲的关键技术：

1、设置显示图元控件的几个属性： 必须要设置，否则效果不是很明显！
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw |ControlStyles.AllPaintingInWmPaint, true);

2、窗口刷新一次的过程中，让所有图元同时显示到窗口。

可以通过以下几种方式实现，这几种方式都涉及到Graphics对象的创建方式。
Graphics对象的创建方式：

a、在内存上创建一块和显示控件相同大小的画布，在这块画布上创建Graphics对象。接着所有的图元都在这块画布上绘制，绘制完成以后再使用该画布覆盖显示控件的背景，从而达到“显示一次仅刷新一次”的效果！

b、直接在内存上创建Graphics对象：
\subsection{只安装MSVC编译器}
在虚拟机当中进行代码的测试，但是发现VS的安装文件实在太大了，萌生了只安装编译器的想法，因为自己也不使用别的东西。在网上搜索了一下，真有相关的资料。

\subsection{Qt的元对象系统}

\subsection{qflags}
在软件当中，有的时候就会有很多的标志作为判断，比如保存图形有没有被选中，是不是可见，等等，需要一个变量来保存状态，如果每一个都采用单独的变量，那么肯定非常的浪费空间，最合力的方法还是用bit位操作进行。Qt当中有一个QFlag的类，专门用来

QFlags的本质
QFlags(通常称为标志)是Qt中内置的模板类，其主要作用是为枚举值及其组合运算提供类型安全的算法。比如有如下枚举
enum E{a=1, b=2, c=3};
则void f(E);可接受枚举类型E的成员值，但是不能接受其组合运算的值，比如f(a|b);是错误的，因为a|b的结果是int型而不是类型E。若把函数声明为void f(int);则可以接受枚举值的组合运算，但是同时还能接受整型等其他类型，这是类型不安全的行为。
QFlags类就是为了解决以上问题的，比如f(QFlags< E>); 则f就只能接受类型为E的成员值的组合运算。由此可见，QFlags通常与枚举类型是一起使用的。
在Qt中通常把标志使用typedef进行重新命名(因为QFlags是模板类其类型名太长)，重命名之后的名字与对应的枚举类型名字通常只在末尾相差一个字母s，比如与枚举类型E对应的标志通常被重命名为Es，其语法为typedef QFlags< E> Es;
综上所述，在Qt中，若函数的形参是标志，则实参可以是多个按位“或”的枚举类型值，若函数的参数是枚举，则只能指定该枚举类型的单个值，不能使用按位“或”运算符。
\subsection{connect函数}
为了连接信号与槽，应当使用connect函数。使用connect函数的时机应当是在变量构造完成之后，否则，达不到预期的效果，不能建立正确的连接。

软件当中各种信号，应该不是一开始就考虑到的，而是开发过程中，为了解决某个问题，才采用信号槽这种方式来解决的。所以，这种方式主要能解决什么类型的问题？

信号:

当对象改变其状态时，信号就由该对象发射 (emit) 出去，而且对象只负责发送信号，它不知道另一端是谁在接收这个信号。这样就做到了真正的信息封装，
能确保对象被当作一个真正的软件组件来使用。

槽:

用于接收信号，而且槽只是普通的对象成员函数。
一个槽并不知道是否有任何信号与自己相连接。而且对象并不了解具体的通信机制。

信号与槽的连接:

所有从 QObject 或其子类 ( 例如 Qwidget ) 继承的类都能够包含信号和槽。
因为信号与槽的连接是通过 QObject 的 connect() 成员函数来实现的。

connect(sender, SIGNAL(signal), receiver, SLOT(slot));


如下例中的:

QObject::connect(button, SIGNAL(clicked()), \&a, SLOT(quit()));

其中，SIGNAL() 与 SLOT() 是转换信号与槽的宏。
注意:

一个信号可以连接多个槽，

当信号发生，会以不确定的顺序一个接一个的调用各个槽。

多个信号可以连接同一个槽，
即无论是发生哪个信号，都会调用这个槽。

信号之间可以相互连接，
第一个信号发生，也会发生第二个信号。

连接可以被移除，
这种情况用得比较少，因为对象被销毁时，Qt会自动移除与这个对象相关的所有连接。代码如下：

disconnect(sender, SIGNAL(signal), receiver, SLOT(slot));
如果一个信号与多个槽相联系的话，那么，当这个信号发生时，与之相关的槽被调用的顺序将是随机的。
宏定义不能用在 signal 和 slot 的参数中。

信号和槽的参数个数与类型必须一致。

凡是定义信号槽的类都必须写Q\_OBJECT宏。
只有加入了Q\_OBJECT，你才能使用QT中的signal和slot机制。

这篇文章来自于 A Deeper Look at Signals and Slots，Scott Collins 2005.12.19。需要说明的是，我们这里所说的“信号槽”不仅仅是指 Qt 库里面的信号槽，而是站在一个全局的高度，从系统的角度来理解信号槽。所以在这篇文章中，Qt 信号槽仅仅作为一种实现来介绍，我们还将介绍另外一种信号槽的实现——boost::signal。因此，当你在文章中看到一些信号的名字时，或许仅仅是为了描述方便而杜撰的，实际并没有这个信号。

什么是信号槽？

这个问题我们可以从两个角度来回答，一个简短一些，另外一个则长些。

让我们先用最简洁的语言来回答这个问题——什么是信号槽？

信号槽是观察者模式的一种实现，或者说是一种升华；
一个信号就是一个能够被观察的事件，或者至少是事件已经发生的一种通知；
一个槽就是一个观察者，通常就是在被观察的对象发生改变的时候——也可以说是信号发出的时候——被调用的函数；
你可以将信号和槽连接起来，形成一种观察者-被观察者的关系；
当事件或者状态发生改变的时候，信号就会被发出；同时，信号发出者有义务调用所有注册的对这个事件（信号）感兴趣的函数（槽）。
信号和槽是多对多的关系。一个信号可以连接多个槽，而一个槽也可以监听多个信号。

信号可以有附加信息。例如，窗口关闭的时候可能发出 windowClosing 信号，而这个信号就可以包含着窗口的句柄，用来表明究竟是哪个窗口发出这个信号；一个滑块在滑动时可能发出一个信号，而这个信号包含滑块的具体位置，或者新的值等等。我们可以把信号槽理解成函数签名。信号只能同具有相同签名的槽连接起来。你可以把信号看成是底层事件的一个形象的名字。比如这个 windowClosing 信号，我们就知道这是窗口关闭事件发生时会发出的。

信号槽实际是与语言无关的，有很多方法都可以实现信号槽，不同的实现机制会导致信号槽差别很大。信号槽这一术语最初来自 Trolltech 公司的 Qt 库（现在已经被 Nokia 收购）。1994年，Qt 的第一个版本发布，为我们带来了信号槽的概念。这一概念立刻引起计算机科学界的注意，提出了多种不同的实现。如今，信号槽依然是 Qt 库的核心之一，其他许多库也提供了类似的实现，甚至出现了一些专门提供这一机制的工具库。

简单了解信号槽之后，我们再来从另外一个角度回答这个问题：什么是信号槽？它们从何而来？

前面我们已经了解了信号槽相关的概念。下面我们将从更细致的角度来探讨，信号槽机制是怎样一步步发展的，以及怎样在你自己的代码中使用它们。

程序设计中很重要的一部分是组件交互：系统的一部分需要告诉另一部分去完成一些操作。让我们从一个简单的例子开始：

\begin{lstlisting}
// C++
class Button
{
public:
void clicked(); // something that happens: Buttons may be clicked
};
class Page
{
public:
void reload(); // ...which I might want to do when a Button is clicked
};
\end{lstlisting}
换句话说，Page 类知道如何重新载入页面（reload），Button 有一个动作是点击（click）。假设我们有一个函数返回当前页面 currentPage()，那么，当 button 被点击的时候，当前页面应该被重新载入。

\begin{lstlisting}
// C++ --- making the connection directly
void Button::clicked()
{
currentPage()->reload(); // Buttons know exactly what to do when clicked
}
\end{lstlisting}
这看起来并不很好。因为 Button 这个类名似乎暗示了这是一个可重用的类，但是这个类的点击操作却同 Page 紧紧地耦合在一起了。这使得只要 button 一被点击，必定调用 currentPage() 的 reload() 函数。这根本不能被重用，或许把它改名叫 PageReloadButton 更好一些。

实际上，不得不说，这确实是一种实现方式。如果 Button::click() 这个函数是 virtual 的，那么你完全可以写一个新类去继承这个 Button：

\begin{lstlisting}
// C++ --- connecting to different actions by specializing
class Button
{
public:
virtual void clicked() = 0; // Buttons have no idea what to do when clicked
};

class PageReloadButton : public Button
{
public:
virtual void clicked() {
currentPage()->reload();    // ...specialize Button to connect it to a specific action
}
};
\end{lstlisting}
好了，现在 Button 可以被重用了。但是这并不是一个很好的解决方案。

引入回调

让我们停下来，回想一下在只有 C 的时代，我们该如何解决这个问题。如果只有 C，就不存在 virtual 这种东西。重用有很多种方式，但是由于没有了类的帮助，我们采用另外的解决方案：函数指针。
\begin{lstlisting}
/* C --- connecting to different actions via function pointers */
void reloadPage_action( void* ) /* one possible action when a Button is clicked */
{
reloadPage(currentPage());
}

void loadPage_action( void* url ) /* another possible action when a Button is clicked */
{
loadPage(currentPage(), (char*)url);
}

struct Button {
/* ...now I keep a (changeable) pointer to the function to be called */
void (*actionFunc_)();
void* actionFuncData_;
};

void buttonClicked( Button* button )
{
/* call the attached function, whatever it might be */
if ( button && button->actionFunc_ )
(*button->actionFunc_)(button->actionFuncData_);
}
\end{lstlisting}

这就是通常所说的“回调”。buttonClicked() 函数在编译期并不知道要调用哪一个函数。被调用的函数是在运行期传进来的。这样，我们的 Button 就可以被重用了，因为我们可以在运行时将不同的函数指针传递进来，从而获得不同的点击操作。

增加类型安全

对于 C++ 或者 Java 程序员来说，总是不喜欢这么做。因为这不是类型安全的（注意 url 有一步强制类型转换）。

我们为什么需要类型安全呢？一个对象的类型其实暗示了你将如何使用这个对象。有了明确的对象类型，你就可以让编译器帮助你检查你的代码是不是被正确的使用了，如同你画了一个边界，告诉编译器说，如果有人越界，就要报错。然而，如果没有类型安全，你就丢失了这种优势，编译器也就不能帮助你完成这种维护。这就如同你开车一样。只要你的速度足够，你就可以让你的汽车飞起来，但是，一般来说，这种速度就会提醒你，这太不安全了。同时还会有一些装置，比如雷达之类，也会时时帮你检查这种情况。这就如同编译器帮我们做的那样，是我们出浴一种安全使用的范围内。

回过来再看看我们的代码。使用 C 不是类型安全的，但是使用 C++，我们可以把回调的函数指针和数据放在一个类里面，从而获得类型安全的优势。例如：


\begin{lstlisting}
// re-usable actions, C++ style (callback objects)
class AbstractAction
{
public:
virtual void execute() = 0; // sub-classes re-implement this to actually do something
};

class Button
{
// ...now I keep a (changeable) pointer to the action to be executed
AbstractAction* action\_;
};

void Button::clicked()
{
// execute the attached action, whatever it may be
if ( action_ )
action_->execute();
}

class PageReloadAction : public AbstractAction
// one possible action when a Button is clicked
{
public:
virtual void execute() {
currentPage()->reload();
}
};
class PageLoadAction : public AbstractAction
// another possible action when a Button is clicked
{
public:
// ...
virtual void execute() {
currentPage()->load(url_);
}
private:
std::string url_;
};
\end{lstlisting}
好了！我们的 Button 已经可以很方便的重用了，并且也是类型安全的，再也没有了强制类型转换。这种实现已经可以解决系统中遇到的绝大部分问题了。似乎现在的解决方案同前面的类似，都是继承了一个类。只不过现在我们对动作进行了抽象，而之前是对 Button 进行的抽象。这很像前面 C 的实现，我们将不同的动作和 Button 关联起来。现在，我们一步步找到一种比较令人满意的方法。
\subsection{Linux为什么不能运行exe？}
那Linux可不可以运行exe格式的文件呢？当然是可以的，只要按照exe文件格式把它加载到内存然后把IP指针指过去就行了。为难之处在于：exe必然有大量的系统调用。Linux自然无法提供这些系统调用，那就没有办法运行喽。如果想运行exe的话，重点其实不在于exe文件格式，以及怎么调入内存。重点在于可以系统能不能提供这些系统调用。可这谈何容易？相当于是把整个Windows做了一遍。但即使是这样的东西也仍然有人做出来了，就是Wine。那为什么wine无法运行所有exe呢？大概有两个原因第一是因为有人不守规矩，一方面是微软系统调用实现的方式跟手册上写的不一样（最近很少了），一方面是很多国产厂商倾向于使用各种“未公开的API”或者一些非正常的手段来实现一些罕见的功能来彰显自己的技术（最近也少了）。第二则是windows的杀手锏之一DirectX，这个暂时开源实现还有困难。这就导致了其实Windows也不能完美的运行所有的exe文件。比如Win3.1/9x运行DOS的exe就有可能出问题。XP运行9x的exe也可能出问题。要不咋有那么多人赖着不升级呢。所以总体来说就是越守规矩的exe越容易拿到Linux下运行。国内那些不太守规矩的玩意（比如QQ），在对wine针对性优化之后也是可以运行的。比如deepin版的wine。
\subsection{Qt如何保持子窗口显示在最上面的两种方式}
在Qt开发桌面软件的过程中，根据开发的需求不同，我们经常需要将弹出窗口，一般常见的是需要是以下两种。

1.锁定弹出的窗口，阻塞其他窗口（包括主窗口）的操作，只有关闭当前窗口才能点击其他窗口或者主窗口

2.保持当前窗口一直显示在最顶层，但是不锁定（即同时可以操作其他窗口），同时也需要保证不影响其他程序

\begin{itemize}
	\item 如果需要嵌入至其他窗体中，则基于QWidget创建；
	\item 如果是顶级对话框，则基于QDialog创建；
	\item 如果是主窗体，则基于QMainWindow创建；
	\item QDialog又分为非模态对话框、模态对话框、半模态对话框。
\end{itemize}
\subsection{qt如何动态的翻译？}
现在知道的翻译方法都是固定的在字符串前面加tr函数，但是如果我从文件当中读入一个字符串，想要翻译怎么办？

另一种方法：

直接提供翻译的json，然后以一种格式命名，例如：data\_zhCN.json，data\_enUS.json，data\_hkCN.json

然后在tr里面提供一个字符串，例如：QString suffix=tr("enUS"); 并且在ts中将其翻译为zhCN、hkCN等

以这个后缀拼接文件名：QString("data\_\%1.json").arg(suffix)来调用对应翻译好的json，也可以解决这个问题。

说一下思路吧：

1、json中读取的应该是固定的字符串，不论是key还是value。那么这些字符串应该是有个全集的。

2、在Qt中建立QHash，对应的是：QHash<QString, QString> translationMap;

translationMap.append("原始文字", tr("原始文字"));

生成对应的翻译Hash Map

3、对于每个读取到的东西，直接去Hash中查，然后输出Hash Map的结果就好。
\subsection{pop\_back}
pop\_back函数会调用成员的析构函数。具体调试的方法是，在对应成员的析构函数当中设置断点或者打印输出。另外qtcreator在调试的时候，似乎无法显示private类型的变量值。

\subsection{qt 翻译文件，清除不存在的条目}
you can call lupdate with the argument -noobsolete. 
\subsection{插件的生命周期}
PluginManager管理插件IPlugin的生命周期，从初始化插件到删除插件共八个状态。插件状态表——PluginSpec::enum State { Invalid, Read, Resolved, Loaded, Initialized, Running, Stopped, Deleted};
插件IPlugin的状态记录在与插件对应的插件说明PluginSpec中，状态切换过程如下：
\begin{enumerate}
    \item 当new PluginSpec时，还没有插件与之关联，此时PluginSpec记录的插件状态为无效（Invalid）；
    \item 读取插件文件后，状态切换成已读（Read）；
    \item 读取全部插件后，首先需要解决插件之间的依赖问题，解决完毕后，状态切换成已解决（Resolved）；
    \item 然后安装依赖顺序依次加载插件，状态切换成已加载（Loaded）；
    \item 初始化所有插件，状态切换成已初始化（Initialized）；
    \item 运行插件，状态切换成正在运行（Running）；
    \item 当QtCreator退出时，插件依次退出，状态切换成（Stopped）；
    \item 全部退出后，删除插件，状态切换成（Deleted）。
\end{enumerate}

在PluginManager::setPluginPaths(pluginPaths);中完成插件由“无效状态”到“已读状态”再到“解决依赖状态”的转变，详细过程如下：

【1】PluginManager遍历插件路径，并创建插件说明PluginSpec，此时插件状态为初始值：【Invalid】；
\begin{lstlisting}
 （PluginManager::setPluginPaths(pluginPaths) --> readPluginPaths() --> foreach{PluginSpec *spec = new PluginSpec} --> PluginSpec::State state = PluginSpec::Invalid）   
\end{lstlisting}


【2】在PluginSpec读取插件文件中的元信息QPluginLoader::metaData()（元信息类型QJsonObject）后，插件状态由【Invalid】变成【Read】；
\begin{lstlisting}
（PluginManager::setPluginPaths(pluginPaths) --> readPluginPaths() --> foreach{spec->d->read(pluginFile)} --> loader.setFileName(filePath) --> readMetaData(loader.metaData())）    
\end{lstlisting}


【3】读取所有插件文件后，需要解决插件之间的依赖关系和顺序，处理完毕后插件状态由【Read】变成【Resolved】
\begin{lstlisting}
（PluginManager::setPluginPaths(pluginPaths) --> readPluginPaths() --> resolveDependencies() --> foreach{spec->d->resolveDependencies(pluginSpecs)}）  
\end{lstlisting}

在PluginManager::loadPlugins();中完成插件由“加载状态”到“初始化状态”再到“运行状态”的转变，详细过程如下：

【4】loadPlugins中遍历插件说明PluginSpec，并执行QPluginLoader::load()，插件状态由【Resolved】变成【Loaded】
\begin{lstlisting}
（foreach{loadPlugin(spec, PluginSpec::Loaded)} --> spec->d->loadLibrary() --> loader.load()）；    
\end{lstlisting}


【5】loadPlugins中遍历插件说明PluginSpec，并执行
\begin{lstlisting}
IPlugin::initialize(arguments, &err)    
\end{lstlisting}
，插件状态由【Loaded】变成【Initialized】
\begin{lstlisting}
（foreach{loadPlugin(spec, PluginSpec::Initialized)} --> spec->d->initializePlugin() --> plugin->initialize(arguments, &err)）；
\end{lstlisting}


【6】loadPlugins中反向遍历插件说明PluginSpec，并执行IPlugin::extensionsInitialized()，插件状态由【Initialized】变成【Running】
\begin{lstlisting}
（Utils::reverseForeach{loadPlugin(spec, PluginSpec::Initialized)} --> spec->d->initializeExtensions() --> plugin->extensionsInitialized()）；    
\end{lstlisting}

插件在收到QtCreator发出的退出信号后，执行退出操作。
\begin{lstlisting}
QObject::connect(&app, &QCoreApplication::aboutToQuit, &pluginManager, &PluginManager::shutdown);
\end{lstlisting}

【7】当程序退出时，发出信号QCoreApplication::aboutToQuit，执行槽函数PluginManager::shutdown；

遍历插件说明PluginSpec，并将状态由【Running】变成【Stopped】然后执行IPlugin::aboutToShutdown()；

为了等待插件退出，执行shutdownEventLoop->exec()，当所有插件退出后，再继续往下运行；
当插件IPlugin准备好退出时，会发送信号IPlugin::asynchronousShutdownFinished()，通知槽函数PluginManagerPrivate::asyncShutdownFinished()执行；

当所有的插件都发来信号后，调用shutdownEventLoop->exit()，退出事件循环，继续往下运行。
\begin{lstlisting}
（PluginManager::shutdown --> stopAll() --> foreach{loadPlugin(spec, PluginSpec::Stopped)} --> spec->d->stop() --> plugin->aboutToShutdown()）    
\end{lstlisting}


【8】当所有的插件都停止并退出后，反向遍历插件说明PluginSpec，并删除插件delete IPlugin，插件状态由【Stopped】变成【Deleted】
\begin{lstlisting}
（PluginManager::shutdown --> deleteAll() --> Utils::reverseForeach{loadPlugin(spec, PluginSpec::Deleted)} --> spec->d->kill() --> delete plugin）    
\end{lstlisting}

\subsection{Qt 模态对话框与非模态对话框及生命周期}
如果要设置为模态对话框，最简单的就是使用exec()方法，示例代码如下：
\begin{lstlisting}
MyDialog myDlg;   
myDlg.exec();
\end{lstlisting}
        
也可以使用show()方法，示例代码如下：
\begin{lstlisting}
MyDialog myDlg;   
myDlg.setModal(true); 
myDlg.show();
\end{lstlisting}
        
如果要设置为非模态对话框，必须使用show()方法，示例代码如下：
\begin{lstlisting}
MyDialog myDlg; 
myDlg.setModal(false);//或者   myDlg.setModal();
myDlg.show();
\end{lstlisting}
        
如果需要它总在所有窗口的最前面，这时可以通过如下代码设置：
\begin{lstlisting}
myDlg.setWindowFlags(Qt::WindowStaysOnTopHint) 
\end{lstlisting}
        
一闪而过问题分析：
\begin{lstlisting}
MyDialog myDlg;   
myDlg.setModal(true); 
myDlg.show();   
\end{lstlisting}
一闪而过。

myDlg创建在stack上，生命期是大括号内
\begin{lstlisting}
MyDialog *myDlg=new MyDailog();   
myDlg->setModal(true);   //false也可以
myDlg->show();    
\end{lstlisting}
myDlg通过new创建在heap上， 在程序退出时才会被析构。
\subsection{Qt的内存释放策略（内存自动释放机制）}
很多C/C++初学者常犯的一个错误就是，使用malloc、new分配了一块内存却忘记释放，导致内存泄漏。Qt的对象模型提供了一种Qt对象之间的父 子关系，当很多个对象都按一定次序建立起来这种父子关系的时候，就组织成了一颗树。当delete一个父对象的时候，Qt的对象模型机制保证了会自动的把 它的所有子对象，以及孙对象，等等，全部delete，从而保证不会有内存泄漏的情况发生。

任何事情都有正反两面作用，这种机制看上去挺好，但是却会对很多Qt的初学者造成困扰，我经常给别人回答的问题是：1，new了一个Qt对象之后，在什么 情况下应该delete它？2，Qt的析构函数是不是有bug？3，为什么正常delete一个Qt对象却会产生segment fault？等等诸如此类的问题，这篇文章就是针对这个问题的详细解释。

在每一个Qt对象中，都有一个链表，这个链表保存有它所有子对象的指针。当创建一个新的Qt对象的时候，如果把另外一个Qt对象指定为这个对象的父对象， 那么父对象就会在它的子对象链表中加入这个子对象的指针。另外，对于任意一个Qt对象而言，在其生命周期的任何时候，都还可以通过setParent函数 重新设置它的父对象。当一个父对象在被delete的时候，它会自动的把它所有的子对象全部delete。当一个子对象在delete的时候，会把它自己 从它的父对象的子对象链表中删除。

QWidget是所有在屏幕上显示出来的界面对象的基类，它扩展了Qt对象的父子关系。一个Widget对象也就自然的成为其父Widget对象的子 Widget，并且显示在它的父Widget的坐标系统中。例如，一个对话框(dialog)上的按钮(button)应该是这个对话框的子 Widget。

关于Qt对象的new和delete，下面我们举例说明。

例如，下面这一段代码是正确的：
\begin{lstlisting}
int main()
{
QObject* objParent = new QObject(NULL);
QObject* objChild = new QObject(objParent);
QObject* objChild2 = new QObject(objParent);
delete objParent;
}    
\end{lstlisting}
在上述代码片段中，objParent是objChild的父对象，在objParent对象中有一个子对象链表，这个链表中保存它所有子对象的指针，在 这里，就是保存了objChild和objChild2的指针。在代码的结束部分，就只有delete了一个对象objParent，在objParent对象的析构函数会遍历它的子对象链表，并且把它所有的子对象(objChild和objChild2)一一删除。所以上面这段代码是安 全的，不会造成内存泄漏。

如果我们把上面这段代码改成这样，也是正确的：
\begin{lstlisting}
int main()
{
QObject* objParent = new QObject(NULL);
QObject* objChild = new QObject(objParent);
QObject* objChild2 = new QObject(objParent);
delete objChild;
delete objParent;
}    
\end{lstlisting}

在这段代码中，我们就只看一下和上一段代码不一样的地方，就是在delete objParent对象之前，先delete objChild对象。在delete objChild对象的时候，objChild对象会自动的把自己从objParent对象的子对象链表中删除，也就是说，在objChild对象被 delete完成之后，objParent对象就只有一个子对象(objChild2)了。然后在delete objParent对象的时候，会自动把objChild2对象也delete。所以，这段代码也是安全的。

Qt的这种设计对某些调试工具来说却是不友好的，比如valgrind。比如上面这段代码，valgrind工具在分析代码的时候，就会认为objChild2对象没有被正确的delete，从而会报告说，这段代码存在内存泄漏。哈哈，我们知道，这个报告是不对的。

我们在看一看这一段代码：
\begin{lstlisting}
int main()
{
QWidget window;
QPushButton quit("Exit", window);
}    
\end{lstlisting}

在这段代码中，我们创建了两个widget对象，第一个是window，第二个是quit，他们都是Qt对象，因为QPushButton是从QWidget派生出来的，而QWidget是从QObject派生出来的。这两个对象之间的关系是，window对象是quit对象的父对象，由于他们 都会被分配在栈(stack)上面，那么quit对象是不是会被析构两次呢？我们知道，在一个函数体内部声明的变量，在这个函数退出的时候就会被析构，那 么在这段代码中，window和quit两个对象在函数退出的时候析构函数都会被调用。那么，假设，如果是window的析构函数先被调用的话，它就会去 delete quit对象；然后quit的析构函数再次被调用，程序就出错了。事实情况不是这样的，C++标准规定，本地对象的析构函数的调用顺序与他们的构造顺序相 反。那么在这段代码中，这就是quit对象的析构函数一定会比window对象的析构函数先被调用，所以，在window对象析构的时候，quit对象已 经不存在了，不会被析构两次。

如果我们把代码改成这个样子，就会出错了，对照前面的解释，请你自己来分析一下吧。
\begin{lstlisting}
int main()
{
QPushButton quit("Exit");
QWidget window;
quit.setParent(window);
}    
\end{lstlisting}

但是我们自己在写程序的时候，也必须重点注意一项，千万不要delete子对象两次，就像前面这段代码那样，程序肯定就crash了。

最后，让我们来结合Qt source code，来看看这parent/child关系是如何实现的。

在本专栏文章的第一部分“对象数据存储”，我们说到过，所有Qt对象的私有数据成员的基类是QObjectData类，这个类的定义如下：
\begin{lstlisting}
typedef QList&lt;QObject*&gt; QObjectList;
class QObjectData
{
public:
QObject *parent;
QObjectList children;
// 忽略其它成员定义
};    
\end{lstlisting}

我们可以看到，在这里定义了指向parent的指针，和保存子对象的列表。其实，把一个对象设置成另一个对象的父对象，无非就是在操作这两个数据。把子对 象中的这个parent变量设置为指向其父对象；而在父对象的children列表中加入子对象的指针。当然，我这里说的非常简单，在实际的代码中复杂的 多，包含有很多条件判断，有兴趣的朋友可以自己去读一下Qt的源代码。
\subsection{QStandardItemModel}
\subsection{插件的编写原则}
写插件的时候，应当遵循什么思想？插件应该是为主程序服务的吧？所以，它仅有的参数应当来自于已有的插件。应当尽量不需要修改什么程序，我的插件放进去就直接能好使。qtcreator的右键菜单处理的很简单，因为它并没有很复杂的文件类型。仔细一看，原理qtcreator的右键菜单也不完全的相同，然后看了一下实现原理，原来是检测projecttree的currentNodeChanged，然后动态地更改菜单。看来我的实现确实不够插件化。并不是在主插件当中把所有的node都定义出来。而是，分散，分散。在插件当中，继承顶部的node，再运行时判断是不是子类，如果是的话，就将相关的菜单进行显示，否则，隐藏。看来这一块还需要好好地实现啊。采用这种方法的话实际只需要管理leaf的右键菜单就可以了。至于具体地菜单是怎样的，那就看最后添加成什么样了。只需要将相关的设为可见或者不可见就可以了。更简单的，只需要在updateContextActions函数当中判断一下node的具体类型，就直接选择某一个group的菜单进行显示就OK了。对，应该可行，这才是正确的思路。插件在编写的时候，应该时刻铭记，你要时刻向主程序提供服务，而不是在某些方面要让主插件依赖你。但是这样可能带来一个问题，每一次更新的时候，都需要判断一次菜单是否改变，这会不会影响速度？但是我估计，node的种类不会很多，顶多执行几个函数，这样。

另外一个问题就是，插件如何获取主插件的数据？我弹出了一个右键菜单，但是执行的时候，需要考虑其他插件的数据，这个时候应该怎么办？
\subsection{如何用插件在tree上创建一个子节点？}
可以在插件当中继承出自己的node，那么如何将node显示在tree当中？思路是怎么样的？感觉是这个东西应该是要加入到什么或者注册到什么当中。目前还不清楚。感觉是registerTreeManager函数。

创建子节点还是应该用现在的createtree的方法，手动地去调用，不同的是，现在每一个node都是父类的Node类型，通过制定nodetype来进行区分，而实际应该是用具体的继承后的子node类型来进行创建，这样后续就可以调用不同的接口了。

那新的问题，就是如何创建nodechange的信号？好像是利用tree控件的itemchange信号操作的。目前不是问题。

之前自己似乎没有真的理解清楚各个插件之间的依赖关系。其实具体的project的级别才是最低的，它依赖于几何、分网、后处理插件，需要借助这么些工具来实现各种的操作。
\subsection{如何用qtcreator打开cmake项目？}
跟一般的IDE不同，cmake是一种不需要操作界面的管理项目的方法，非常的流行。在网上看到很多qt项目是用cmake管理的，但是自己想要尝试在编辑器中打开这个项目，毕竟不知道怎么跳转函数。于是就研究了一下如何用qtcreator打开cmake管理的项目。

该操作需要使用命令行操作，在cmd窗口中，跳转到你的项目所在的目录，转换命令为

qmake -project

如果报错找不到cl命令，那么根据提示你需要运行一下
\begin{listlsting}
"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86_amd64    
\end{listlsting}
因为路径当中含有空格，所以需要加引号。这样生成的项目，也就是能正常的看而已，编译还是不能的。

\subsection{更新右键菜单}
一种可以用来更新右键菜单的流程。界面操作，node发生改变，发出信号currentNodeChanged()，然后执行绑定的动作事件updateContextActions()来实现对应的右键菜单的改变。在具体地实现里，通过project插件，获取到当前的node，然后动态判断node的类型，根据类型设置相关的动作的可用或者可见。原来是这样。

按照这样的逻辑编写的话，那么现在就需要重新地修改一些node的定义。之前都是不断地通过添加nodetype的方式来实现的，那种方式实在太傻了。必须采用继承的方式去做，这样才能完成动态类型的判断。

那么时候时候会发出currentNodeChanged的信号？由setCurrent衷发出，而setCurrent被两个函数调用，updateFromNode和updateFromProjectTreeWidget，而这些又是在哪里被调用的呢？

右键菜单是全局的，每个插件都可以更新它。可以将右键菜单简单的分类，文件夹的，leaf的，所有的右键操作都方面，运行的时候根据node的类型进行自动判断。

有一个问题，如果右键的动作和顶部菜单的动作一样呢？那么在右键菜单里判断了，顶层菜单的动作状态岂不是改变了？所以就涉及到如何更改顶部的状态。感觉要分开，不能用一个action来表示，这样二者的状态必然是同步的。貌似只有ribbon有这个问题，因为传统的菜单和右键菜单是不可能同时存在的，哈哈。看了qtcreator的实现，确实是定义了两个action。必须要分开。

插件的依赖关系可以改变插件的初始化顺序。

\subsection{libpng warning: iCCP: known incorrect sRGB profile}
Libpng-1.6在检查ICC配置文件方面比以前的版本更加严格。您可以忽略该警告。要摆脱它，请从PNG图像中删除iCCP块。

有些应用程序将警告视为错误。如果您使用的是这样的应用程序，则必须删除该块。您可以使用各种PNG编辑器（例如ImageMagick的

convert in.png out.png

要从文件夹（目录）中所有PNG文件中删除无效的iCCP块，可以mogrify在ImageMagick中使用：

mogrify *.png

这要求您的ImageMagick是使用libpng16构建的。您可以通过运行以下命令轻松检查它：

convert -list format | grep PNG

如果您想找出哪些文件需要修复而不是盲目地处理所有文件，则可以运行

pngcrush -n -q *.png

其中-n均值不重写文件，并且-q均值禁止除警告以外的大多数输出​​。抱歉，pngcrush中除了警告以外，没有其他选项可以禁止显示所有内容。


